#include <gdt.h>

//----------------//
// Local GDT Data //
//----------------//
#define GDT_COUNT 5

//-----------------//
// Global GDT data //
//-----------------//
struct gdt_entry gdt[GDT_COUNT];
struct gdt_pointer gdt_p;

//--------------------------//
// Sets an entry in the GDT //
//--------------------------//
void gdt_set_gate(int num, unsigned long BASE, unsigned long LIMIT, unsigned char CODE, unsigned char DPL, unsigned char RW) {
    if(DPL > 3) DPL = 3; // Check DPL
    if(RW > 1) RW = 1;   // Check Read/Write
    
    gdt[num].limit_low = (LIMIT & 0xFFFF);
    gdt[num].base_low = (BASE & 0xFFFFFF);
    gdt[num].accessed = 0;
    gdt[num].read_write = RW;
    gdt[num].conforming_expand_down = 0;
    gdt[num].code = CODE;
    gdt[num].always_1 = 1;
    gdt[num].DPL = DPL;
    gdt[num].present = 1;
    gdt[num].limit_high = ((LIMIT >> 16) & 0x0F);
    gdt[num].available = 1;
    gdt[num].always_0 = 0;
    gdt[num].big = 1;
    gdt[num].gran = 1;
    gdt[num].base_high = ((BASE & 0xFF000000) >> 24);
}

//------------------------------//
// Sets a null entry in the GDT //
//------------------------------//
void gdt_set_null_gate(int num) {
    gdt[num].limit_low = 0;
    gdt[num].base_low = 0;
    gdt[num].accessed = 0;
    gdt[num].read_write = 0;
    gdt[num].conforming_expand_down = 0;
    gdt[num].code = 0;
    gdt[num].always_1 = 0;
    gdt[num].DPL = 0;
    gdt[num].present = 0;
    gdt[num].limit_high = 0;
    gdt[num].available = 0;
    gdt[num].always_0 = 0;
    gdt[num].big = 0;
    gdt[num].gran = 0;
    gdt[num].base_high = 0;     
}

//------------------//
// Installs the GDT //
//------------------//
void gdt_install(void) {
    gdt_p.limit = (sizeof(struct gdt_entry) * GDT_COUNT) - 1;
    gdt_p.base = (unsigned)&gdt;
    
    gdt_set_null_gate(0);
    
    // Flat Model
    gdt_set_gate(1, 0, 0xFFFFFFFF, 1, 0, 1); // KERNEL CODE
    gdt_set_gate(2, 0, 0xFFFFFFFF, 0, 0, 1); // KERNEL DATA   
    
    gdt_set_gate(3, 0, 0xFFFFFFFF, 1, 3, 1); // USER CODE
    gdt_set_gate(4, 0, 0xFFFFFFFF, 0, 3, 1); // USER DATA
  
    gdt_flush();
}
